package controllers

import dao._

import javax.inject.Inject
import org.joda.time.DateTime
import play.api.mvc.{AbstractController, ControllerComponents}
import play.api.routing.JavaScriptReverseRouter
import tool.{FormTool, Tool}
import models.Tables._
import play.api.cache.SyncCacheApi
import play.api.i18n.I18nSupport
import shared.pojo.Pojo.{LoginUserData, ValidData}
import tool.Pojo.Info
import utils.Utils

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.concurrent.duration._
import scala.language.postfixOps
import upickle.default._
import implicits.Implicits._
import upickle.default._

/**
 * Created by Administrator on 2019/8/7
 */
class AppController @Inject()(cc: ControllerComponents, cache: SyncCacheApi)(
  implicit val userDao: UserDao,
  implicit val accountDao: AccountDao,
  implicit val aliyunKeyDao: AliyunKeyDao,
  implicit val collectDao: CollectDao,
  implicit val signInDao: SignInDao,
  implicit val personInfoDao: PersonInfoDao,
) extends
  AbstractController(cc) with I18nSupport {

  def loginBefore = Action { implicit request =>
    val kind = request.lang.code
    kind match {
      case "en" => Ok(views.html.en.login())
      case _ => Ok(views.html.login())
    }
  }

  def toAbout = Action { implicit request =>
    Ok(views.html.about())
  }

  def toContact = Action { implicit request =>
    Ok(views.html.contact())
  }

  def phoneLoginBefore = Action { implicit request =>
    val kind = request.lang.code
    kind match {
      case _ => Ok(views.html.phoneLogin())
    }
  }

  def registerBefore = Action { implicit request =>
    val kind = request.lang.code
    kind match {
      case "en" => Ok(views.html.en.register())
      case _ => Ok(views.html.register())
    }
  }

  def emailCheck = Action.async { implicit request =>
    val data = FormTool.emailForm.bindFromRequest.get
    userDao.selectByEmail(data.email).zip(accountDao.selectById1).map { case (optionUser, admin) =>
      optionUser match {
        case Some(y) => Ok(ujson.Obj("valid" -> false))
        case None =>
          val valid = true
          Ok(ujson.Obj("valid" -> valid))
      }
    }
  }

  def register = Action.async { implicit request =>
    val messages = request.messages
    val data = FormTool.registerUserForm.bindFromRequest().get
    val row = UserRow(0, data.fullName, data.password, data.email, new DateTime(), data.phone,
      institution = data.company, province = data.province, city = data.city, teacher = data.teacherName)
    userDao.insert(row).map { userId =>
      Redirect(routes.AppController.loginBefore).flashing("info" -> s"${messages("registerSuccessInfo")}!",
        "class" -> Utils.successClass)
    }
  }

  def registerActiveBefore = Action { implicit request =>
    val data = FormTool.emailForm.bindFromRequest().get
    Ok(views.html.registerActive(data.email))
  }

  def sendValidCode = Action.async { implicit request =>
    val messages = request.messages
    val data = FormTool.phoneForm.bindFromRequest().get
    aliyunKeyDao.selectById1.map { aliyunKeyRow =>
      val (b, code, responseCode) = Utils.sendMessage(data.phone, aliyunKeyRow)
      if (b) {
        cache.set(data.phone, code, 10 minutes)
        Ok(ujson.Obj("valid" -> true))
      } else {
        Ok(ujson.Obj("valid" -> false, "message" -> s"${messages("SMSDeliveryFailure")} ${responseCode}！"))
      }
    }
  }

  def validCode = Action { implicit request =>
    val messages = request.messages
    val data = FormTool.validCodeForm.bindFromRequest().get
    if (cache.get(data.phone).isDefined && cache.get(data.phone).get == data.validCode) {
      cache.remove(data.phone)
      Ok(ujson.Obj("valid" -> true))
    } else {
      Ok(ujson.Obj("valid" -> false, "message" -> messages("ErrorInVerificationCode")))
    }
  }

  def initF(userId: Int) = {
    val collectF = collectDao.selectOp(userId).flatMap { collectOp =>
      val newRow = collectOp.getOrElse {
        CollectRow(userId, List.empty)
      }
      collectDao.insertOrUpdate(newRow)
    }
    val signInF = signInDao.selectOp(userId).flatMap { rowOp =>
      rowOp.map { row =>
        Future {}
      }.getOrElse {
        val newRow = SignInRow(userId, ujson.Obj())
        signInDao.insert(newRow)
      }
    }
    val personInfoF = personInfoDao.selectOp(userId).flatMap { rowOp =>
      rowOp.map { row =>
        Future {}
      }.getOrElse {
        val newRow = PersonInfoRow(userId, 0)
        personInfoDao.insert(newRow)
      }
    }
    collectF.zip(signInF).zip(personInfoF)
  }

  def loginApi = Action.async { implicit request =>
    val messages = request.messages
    val data = Tool.transformRq.transform(reader[LoginUserData])
    val langCode = request.lang.code
    val selectF = langCode match {
      case "en" => userDao.selectOpByEmailAndPassword(data.email, data.password).zip(accountDao.selectById1)
      case _ => userDao.selectOpByEmailOrPhoneAndPassword(data.email, data.password).zip(accountDao.selectById1)
    }
    selectF.map { case (optionUser, accountRow) =>
      val response = if (optionUser.isDefined || (accountRow.account == data.email && accountRow.password == data.password)) {
        ValidData(valid = true)
      } else {
        ValidData(valid = false, message = s"${messages("loginValidError")}!")
      }
      Ok(writeJs(response))
    }
  }

  def login = Action.async { implicit request =>
    val messages = request.messages
    val data = Tool.transformRq.transform(reader[LoginUserData])
    val langCode = request.lang.code
    val selectF = langCode match {
      case "en" => userDao.selectOpByEmailAndPassword(data.email, data.password).zip(accountDao.selectById1)
      case _ => userDao.selectOpByEmailOrPhoneAndPassword(data.email, data.password).zip(accountDao.selectById1)
    }
    selectF.flatMap { case (optionUser, accountRow) =>
      if (optionUser.isDefined) {
        val user = optionUser.get
        initF(user.id).map { x =>
          Redirect(routes.MissionController.missionManageBefore).addingToSession("user" -> user.fullName,
            "id" -> user.id.toString)
        }
      } else if (accountRow.account == data.email && accountRow.password == data.password) {
        Future.successful(
          Redirect(routes.AdminController.metaboDbManageBefore).addingToSession("admin" -> data.email)
        )
      } else {
        Future.successful(
          Redirect(routes.AppController.loginBefore).flashing("info" -> s"${messages("loginValidError")}!", "class" ->
            Utils.errorClass)
        )
      }
    }
  }

  def phoneLogin = Action.async { implicit request =>
    val data = FormTool.phoneForm.bindFromRequest().get
    val messages = request.messages
    userDao.selectOpByPhone(data.phone).map { optionUser =>
      if (optionUser.isDefined) {
        val user = optionUser.get
        Redirect(routes.MissionController.missionManageBefore).addingToSession("user" -> user.fullName,
          "id" -> user.id.toString)
      } else {
        Redirect(routes.AppController.phoneLoginBefore).
          flashing(
            "info" -> s"${messages("phoneRegisterInfo")} <a href=${routes.AppController.registerBefore}>${messages("Register")}</a>!",
            "class" -> Utils.errorClass)
      }
    }
  }

  def recoverPasswordBefore = Action { implicit request =>
    Ok(views.html.recoverPassword())
  }

  def newPasswordEmailValid = Action.async { implicit request =>
    val data = FormTool.emailForm.bindFromRequest().get
    val messages = request.messages
    userDao.selectSomeByEmail(data.email).map { x =>
      Future {
        val codeEmail = Tool.newPasswordDesEncrypter.encrypt(data.email)
        val href = s"http://${request.host}${routes.AppController.newPasswordBefore}?email=${Utils.urlEncode(codeEmail)}"
        val content = messages("newPasswordEmailTemplate", x.fullName, s"<a href='${href}'>${href}</a>")
        val info = Info(s"${messages("NotificationOfPasswordRetrieval")}", content)
        val inbox = data.email
        Utils.sendEmailBySsl(Utils.sender, info, inbox)
      }
      Ok("success!")
    }
  }

  def newPasswordBefore = Action { implicit request =>
    Ok(views.html.newPassword())
  }

  def newPassword = Action.async { implicit request =>
    val messages = request.messages
    val data = FormTool.newPasswordForm.bindFromRequest().get
    userDao.selectByPhone(data.phone).flatMap { x =>
      val row = x.copy(password = data.password)
      userDao.update(row).map { y =>
        Redirect(routes.AppController.loginBefore).flashing("info" -> s"${messages("RetrievePasswordSuccessfully")}", "class" ->
          Utils.successClass).removingFromSession("user")
      }
    }
  }

  def phoneCheck = Action.async {
    implicit request =>
      val data = FormTool.phoneForm.bindFromRequest.get
      userDao.selectOpByPhone(data.phone).zip(accountDao.selectById1).map {
        case (optionUser, admin) =>
          optionUser match {
            case Some(y) => Ok(ujson.Obj("valid" -> false))
            case None =>
              val valid = true
              Ok(ujson.Obj("valid" -> valid))
          }
      }
  }

  def phoneExistCheck = Action.async {
    implicit request =>
      val data = FormTool.phoneForm.bindFromRequest.get
      userDao.selectOpByPhone(data.phone).zip(accountDao.selectById1).map {
        case (optionUser, admin) =>
          optionUser match {
            case Some(y) => Ok(ujson.Obj("valid" -> true))
            case None =>
              val valid = false
              Ok(ujson.Obj("valid" -> valid))
          }
      }
  }

  def javascriptRoutes = Action { implicit request =>
    Ok(
      JavaScriptReverseRouter("jsRoutes")(

        controllers.routes.javascript.AppController.emailCheck,
        controllers.routes.javascript.AppController.newPasswordEmailValid,
        controllers.routes.javascript.AppController.sendValidCode,
        controllers.routes.javascript.AppController.phoneCheck,
        controllers.routes.javascript.AppController.validCode,
        controllers.routes.javascript.AppController.phoneLogin,
        controllers.routes.javascript.AppController.phoneExistCheck,

        controllers.routes.javascript.UserController.getUserById,
        controllers.routes.javascript.UserController.updatePersonInfo,
        controllers.routes.javascript.UserController.phoneChangeCheck,
        controllers.routes.javascript.UserController.securitySetBefore,
        controllers.routes.javascript.UserController.updatePhone,
        controllers.routes.javascript.UserController.emailChangeCheck,
        controllers.routes.javascript.UserController.sendEmailValidCode,
        controllers.routes.javascript.UserController.validEmailCode,
        controllers.routes.javascript.UserController.updateEmail,
        controllers.routes.javascript.UserController.updatePassword,
        controllers.routes.javascript.UserController.updateImage,
        controllers.routes.javascript.UserController.getAvatarImage,
        controllers.routes.javascript.UserController.signIn,
        controllers.routes.javascript.UserController.getHasSignInCurMonth,
        controllers.routes.javascript.UserController.getCoin,
        controllers.routes.javascript.UserController.getHasSignInInfo,

        controllers.routes.javascript.SoftController.getFileContent,
        controllers.routes.javascript.SoftController.downloadExampleData,
        controllers.routes.javascript.SoftController.preDealBefore,
        controllers.routes.javascript.SoftController.annoBefore,
        controllers.routes.javascript.SoftController.pcaBefore,
        controllers.routes.javascript.SoftController.getAllSoft,
        controllers.routes.javascript.SoftController.collect,
        controllers.routes.javascript.SoftController.unCollect,
        controllers.routes.javascript.SoftController.diffAnaBefore,
        controllers.routes.javascript.SoftController.diffAnaMulBefore,
        controllers.routes.javascript.SoftController.fileNameCheck,
        controllers.routes.javascript.SoftController.ccaRdaBefore,
        controllers.routes.javascript.SoftController.heatmapBefore,
        controllers.routes.javascript.SoftController.plsdaBefore,
        controllers.routes.javascript.SoftController.oplsdaBefore,
        controllers.routes.javascript.SoftController.basicBefore,
        controllers.routes.javascript.SoftController.analysis,
        controllers.routes.javascript.SoftController.analysisBefore,
        controllers.routes.javascript.SoftController.vennBefore,
        controllers.routes.javascript.SoftController.preprocessBefore,
        controllers.routes.javascript.SoftController.lefseAnaBefore,
        controllers.routes.javascript.SoftController.limmaAnaBefore,

        controllers.routes.javascript.CollectController.getCollect,

        controllers.routes.javascript.MissionController.missionNameCheck,
        controllers.routes.javascript.MissionController.missionManageBefore,
        controllers.routes.javascript.MissionController.getAllMission,
        controllers.routes.javascript.MissionController.updateMissionSocket,
        controllers.routes.javascript.MissionController.getLogContent,
        controllers.routes.javascript.MissionController.deleteMissionById,
        controllers.routes.javascript.MissionController.downloadResult,
        controllers.routes.javascript.MissionController.resultDetailBefore,
        controllers.routes.javascript.MissionController.getMissionById,
        controllers.routes.javascript.MissionController.getFileData,
        controllers.routes.javascript.MissionController.getResultFileInfo,
        controllers.routes.javascript.MissionController.downloadResultFile,
        controllers.routes.javascript.MissionController.getAllMissionResult,
        controllers.routes.javascript.MissionController.viewCsvFileContent,
        controllers.routes.javascript.MissionController.getAllKind,
        controllers.routes.javascript.MissionController.getSingleColumnAll,
        controllers.routes.javascript.MissionController.reRunMissionBefore,
        controllers.routes.javascript.MissionController.getFileNamesExistInfo,
        controllers.routes.javascript.MissionController.getCcaRdaXInfo,
        controllers.routes.javascript.MissionController.getRowColSize,
        controllers.routes.javascript.MissionController.updateName,
        controllers.routes.javascript.MissionController.deletes,
        controllers.routes.javascript.MissionController.getEdgeInfo,
        controllers.routes.javascript.MissionController.getConfigData,
        controllers.routes.javascript.MissionController.getTrueFileName,
        controllers.routes.javascript.MissionController.getCsvFileInfo,
        controllers.routes.javascript.MissionController.getDiffFeatures,

        controllers.routes.javascript.DataController.downloadExampleData,
        controllers.routes.javascript.DataController.getFileContent,
        controllers.routes.javascript.DataController.getAllFileData,
        controllers.routes.javascript.DataController.deleteFileDataById,
        controllers.routes.javascript.DataController.fileNameNonExistCheck,
        controllers.routes.javascript.DataController.update,
        controllers.routes.javascript.DataController.getFileData,
        controllers.routes.javascript.DataController.getFileDataById,
        controllers.routes.javascript.DataController.downloadFileById,
        controllers.routes.javascript.DataController.detailBefore,
        controllers.routes.javascript.DataController.newGroup,
        controllers.routes.javascript.DataController.deletes,
        controllers.routes.javascript.DataController.getAllExampleData,
        controllers.routes.javascript.DataController.getExampleData,
        controllers.routes.javascript.DataController.getRowCols,
        controllers.routes.javascript.DataController.getNumericColNames,
        controllers.routes.javascript.DataController.getFileDataByFileName,

        controllers.routes.javascript.GroupFileDataController.add,
        controllers.routes.javascript.GroupFileDataController.getAll,
        controllers.routes.javascript.GroupFileDataController.getFileDataById,
        controllers.routes.javascript.GroupFileDataController.detailBefore,
        controllers.routes.javascript.GroupFileDataController.getFileDataById,
        controllers.routes.javascript.GroupFileDataController.getFileData,
        controllers.routes.javascript.GroupFileDataController.deleteFileDataById,
        controllers.routes.javascript.GroupFileDataController.fileNameNonExistCheck,
        controllers.routes.javascript.GroupFileDataController.update,
        controllers.routes.javascript.GroupFileDataController.downloadFileById,
        controllers.routes.javascript.GroupFileDataController.isPaired,
        controllers.routes.javascript.GroupFileDataController.getGroupNum,
        controllers.routes.javascript.GroupFileDataController.getNewFileName,
        controllers.routes.javascript.GroupFileDataController.deletes,
        controllers.routes.javascript.GroupFileDataController.getHeaders,
        controllers.routes.javascript.GroupFileDataController.getGroups,
        controllers.routes.javascript.GroupFileDataController.getCovMap,

        controllers.routes.javascript.MatrixFileDataController.getAll,
        controllers.routes.javascript.MatrixFileDataController.add,
        controllers.routes.javascript.MatrixFileDataController.getHeaders,
        controllers.routes.javascript.MatrixFileDataController.getNewFileName,
        controllers.routes.javascript.MatrixFileDataController.sub,
        controllers.routes.javascript.MatrixFileDataController.deletes,
        controllers.routes.javascript.MatrixFileDataController.update,
        controllers.routes.javascript.MatrixFileDataController.getFileData,
        controllers.routes.javascript.MatrixFileDataController.getFileDataById,
        controllers.routes.javascript.MatrixFileDataController.downloadFileById,
        controllers.routes.javascript.MatrixFileDataController.existSub,
        controllers.routes.javascript.MatrixFileDataController.deleteWithSub,
        controllers.routes.javascript.MatrixFileDataController.detailBefore,
        controllers.routes.javascript.MatrixFileDataController.fileNameNonExistCheck,
        controllers.routes.javascript.MatrixFileDataController.importRun,

        controllers.routes.javascript.VarInfoFileDataController.getAll,
        controllers.routes.javascript.VarInfoFileDataController.add,
        controllers.routes.javascript.VarInfoFileDataController.deletes,
        controllers.routes.javascript.VarInfoFileDataController.update,
        controllers.routes.javascript.VarInfoFileDataController.downloadFileById,
        controllers.routes.javascript.VarInfoFileDataController.getFileDataById,
        controllers.routes.javascript.VarInfoFileDataController.getFileData,
        controllers.routes.javascript.VarInfoFileDataController.detailBefore,
        controllers.routes.javascript.VarInfoFileDataController.fileNameNonExistCheck,

        controllers.routes.javascript.BiomFileDataController.add,
        controllers.routes.javascript.BiomFileDataController.getAll,
        controllers.routes.javascript.BiomFileDataController.getFileDataById,
        controllers.routes.javascript.BiomFileDataController.getFileData,
        controllers.routes.javascript.BiomFileDataController.deletes,
        controllers.routes.javascript.BiomFileDataController.update,
        controllers.routes.javascript.BiomFileDataController.downloadFileById,
        controllers.routes.javascript.BiomFileDataController.detailBefore,
        controllers.routes.javascript.BiomFileDataController.fileNameNonExistCheck,

        controllers.routes.javascript.WorkflowController.missionManageBefore,
        controllers.routes.javascript.WorkflowController.getAllMission,
        controllers.routes.javascript.WorkflowController.getLogContent,
        controllers.routes.javascript.WorkflowController.reRunMissionBefore,
        controllers.routes.javascript.WorkflowController.updateMissionSocket,
        controllers.routes.javascript.WorkflowController.getConfigData,
        controllers.routes.javascript.WorkflowController.deleteMissionById,
        controllers.routes.javascript.WorkflowController.resultDetailBefore,
        controllers.routes.javascript.WorkflowController.getMissionById,
        controllers.routes.javascript.WorkflowController.dataPreprocessBefore,
        controllers.routes.javascript.WorkflowController.getResultFileInfo,
        controllers.routes.javascript.WorkflowController.getFileData,
        controllers.routes.javascript.WorkflowController.downloadResultFile,
        controllers.routes.javascript.WorkflowController.missionNameCheck,
        controllers.routes.javascript.WorkflowController.updateName,
        controllers.routes.javascript.WorkflowController.deletes,
        controllers.routes.javascript.WorkflowController.analysisBefore,
        controllers.routes.javascript.WorkflowController.downloadResult,
        controllers.routes.javascript.WorkflowController.getFsResultFileInfo,
        controllers.routes.javascript.WorkflowController.getDiffNum,
        controllers.routes.javascript.WorkflowController.viewHtml,

        controllers.routes.javascript.MissionToolController.getWorkspaceImage,
        controllers.routes.javascript.MissionToolController.getConfigData,
        controllers.routes.javascript.MissionToolController.plot,
        controllers.routes.javascript.MissionToolController.getColorData,
        controllers.routes.javascript.MissionToolController.getGroups,
        controllers.routes.javascript.MissionToolController.getRowCols,
        controllers.routes.javascript.MissionToolController.getNumericColNames,
        controllers.routes.javascript.MissionToolController.getConfigJsonData,

        controllers.routes.javascript.ToolController.getAllMessages,

        controllers.routes.javascript.AdminController.getAllMetaboDb,
        controllers.routes.javascript.AdminController.updateMetaboDb,

      )
    ).as("text/javascript")

  }


}
